/*

Copyright (C) 2007 Thomas Geiger (tom.geiger@gmail.com)

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

*/

#include <globus_xio_util.h>
#include "globus_gss_assist.h" 

#include "CredManager.h"
#include "GridFTPClient.h"

namespace globus_io {

static bool isValidUrl(const string& url);

static void done_cb(void* user_arg,
					globus_ftp_client_handle_t*	handle,
					globus_object_t* err);

static globus_mutex_t lock;
static globus_cond_t cond;
static globus_bool_t done;
static globus_bool_t error;

static void initLock();
static void waitLock();

GridFTPClient::GridFTPClient()
{
	globus_module_activate(GLOBUS_FTP_CLIENT_MODULE);
    result = globus_ftp_client_handleattr_init(&handle_attr); 
    test_error(result, __LINE__);
	result = globus_ftp_client_handle_init(&handle, &handle_attr);
	test_error(result, __LINE__);
	result = globus_ftp_client_operationattr_init(&operation_attr);
	test_error(result, __LINE__);
	result = globus_ftp_client_operationattr_init(&dest_attr);
	test_error(result, __LINE__);
}

GridFTPClient::~GridFTPClient()
{
	globus_ftp_client_operationattr_destroy(&dest_attr);
	globus_ftp_client_operationattr_destroy(&operation_attr);
	globus_ftp_client_handle_destroy(&handle);
	globus_ftp_client_handleattr_destroy(&handle_attr);
	globus_module_deactivate(GLOBUS_FTP_CLIENT_MODULE);
}

bool GridFTPClient::get(URL* remotefile, ostream data) {
	if (!isValidUrl(remotefile->getURL().c_str())) {
		return false;
	}

	xio_client.setUrl(remotefile);
	bool success = xio_client.receive(data);			
	return success;
}

bool GridFTPClient::put(istream data, URL* remotefile) {
	if (!isValidUrl(remotefile->getURL().c_str())) {
		return false;
	}
	
	xio_client.setUrl(remotefile);
	bool success = xio_client.send(data);
	return success;
}

bool GridFTPClient::transfer(URL* localfile, URL* remotefile) {
	if (!isValidUrl(localfile->getURL().c_str())) {
		return false;
	}
	if (!isValidUrl(remotefile->getURL().c_str())) {
		return false;
	}
	
	initLock();
		
	result = globus_ftp_client_third_party_transfer (&handle, 
												     localfile->getURL().c_str(), 
												     &operation_attr, 
												     remotefile->getURL().c_str(), 
												     &dest_attr, 
												     NULL, 
												     done_cb, 
												     NULL);	
	waitLock();
	test_error(result, __LINE__);
	return !error;
}

bool GridFTPClient::rename(URL* originalfile, string& newfile) {
	return false;
}

bool GridFTPClient::exists(URL* remotefile) {

	if (!isValidUrl(remotefile->getURL().c_str())) {
		return false;
	}

	initLock();
	result = globus_ftp_client_exists(&handle, 
									  remotefile->getURL().c_str(), 
									  &operation_attr, 
									  done_cb, 
									  GLOBUS_SUCCESS);
	waitLock();
	test_error(result, __LINE__);
		
	return !error;
}

bool GridFTPClient::del(URL* remotefile) {
	if (!isValidUrl(remotefile->getURL().c_str())) {
		return false;
	}
	initLock();
	result = globus_ftp_client_delete (&handle, 
									   remotefile->getURL().c_str(), 
									   &operation_attr, 
									   done_cb, 
									   0);
	waitLock();	
	test_error(result, __LINE__);
	
	return !error;
}

int GridFTPClient::size(URL* remotefile) {
	globus_off_t size = -1;

	initLock();

	// globus credential delegate
	
	gss_cred_id_t credential;
	
	if ( !CredManager::acquireCredential(credential) ) {
		cout << "could not aquire cred." << endl;
		return -1;
	}
	
	globus_ftp_client_operationattr_set_authorization(&operation_attr,
										credential,
		                                NULL,
						                NULL,
						              0,
						              remotefile->getHost().c_str());
	
	/*
	globus_ftp_client_operationattr_set_authorization (&operation_attr, 
			credential, 
														NULL, 
														NULL,
														NULL,
														NULL);
	*/
	result = globus_ftp_client_size(&handle,
									remotefile->getURL().c_str(),
	                                &operation_attr,
	                                &size,
	                                done_cb,
	                                NULL);
	
	waitLock();	
	test_error(result, __LINE__);
	
	return size;
}

bool GridFTPClient::mkdir(URL* remotefile) {

	if (!isValidUrl(remotefile->getURL().c_str())) {
		return false;
	}
	initLock();
	result = globus_ftp_client_mkdir (&handle, 
									  remotefile->getURL().c_str(), 
									  &operation_attr, 
									  done_cb, 
									  GLOBUS_SUCCESS);
	waitLock();
	test_error(result, __LINE__);
		
	return !error;
}

bool GridFTPClient::rmdir(URL* remotefile) {

	if (!isValidUrl(remotefile->getURL().c_str())) {
		return false;
	}
	initLock();
	result = globus_ftp_client_rmdir (&handle, 
									  remotefile->getURL().c_str(), 
									  &operation_attr, 
									  done_cb, 
									  GLOBUS_SUCCESS);
	waitLock();
	test_error(result, __LINE__);
		
	return !error;
}

/*
bool GlobusGridFTPClient::list(GlousURL* remotedir, vector<string>& filelist) {
	if (!isValidUrl(remotefile->getURL().c_str())) {
		return false;
	}
	initLock();
	// send list command
	result = globus_ftp_client_list (&handle,
									 remotefile->getURL().c_str(), 
									 &operation_attr, 
									 done_cb, 
									 GLOBUS_SUCCESS);
	waitLock();
	test_error(result, __LINE__);
	
	vector<string> result;
	globus_byte_t buffer[1024*1024];
	std::string result;
	  
	result = globus_ftp_client_register_read( &handle,
	                                          buffer,
	                                          BufferSize_,
	                                          &data_callback,
	                                          &result );
	
	// read command results
	
	globus_ftp_client_register_read (  	
	     	globus_ftp_client_handle_t * 	handle,
	     	globus_byte_t * 	buffer,
	     	globus_size_t 	buffer_length,
	     	globus_ftp_client_data_callback_t 	callback,
	     	void * 	callback_arg)
	     	
	return !error;
}
*/
void GridFTPClient::test_error(globus_result_t res, int line)
{
    if(res == GLOBUS_SUCCESS)
    {
        return;
    }
    
	cerr << "ERROR: " << globus_error_print_friendly( globus_error_peek(res) ) << endl;

    error = GLOBUS_TRUE;
}

static bool isValidUrl(const string& url) {
	string::size_type loc = url.find( "ftp://", 0 );
	if( loc != string::npos ) {
	   return true;
	}
	loc = url.find( "gsiftp://", 0 );
	if( loc != string::npos ) {
		return true;
	}
	
	cerr << "Invalid url, must start with 'ftp://' or 'gsiftp://'" << endl;
	return false;
}

static void done_cb(void* user_arg,
					globus_ftp_client_handle_t*	handle,
					globus_object_t* err)
{
    char * tmpstr;
    error = GLOBUS_FALSE;
    
    if(err)
    {
		tmpstr = globus_object_printable_to_string(err);
		printf("%s\n", tmpstr); 
	    error = GLOBUS_TRUE;
		globus_libc_free(tmpstr);
    }
    globus_mutex_lock(&lock);
    done = GLOBUS_TRUE;
    globus_cond_signal(&cond);
    globus_mutex_unlock(&lock);
       
}

/*
static void data_callback( globus_ftp_client_handle_t* handle,
               			   globus_object_t*            err,
               			   globus_byte_t*              buffer,
               			   globus_size_t               length,
               			   globus_off_t                offset,
               			   globus_bool_t               eof)
{

  if (err != GLOBUS_SUCCESS)
  {
  }
  else
  {    
  
      globus_ftp_client_register_read( handle, 
                                       buffer, 
                                       length, 
                                       data_callback, 
                                       "" );
  }
  
  return;
}
*/
static void initLock() {
	globus_mutex_init(&lock, GLOBUS_NULL);
	globus_cond_init(&cond, GLOBUS_NULL);	
	done = GLOBUS_FALSE;
}

static void waitLock() {
	while(!done)
	{
		globus_cond_wait(&cond, &lock);
	}
	globus_mutex_unlock(&lock);
}

}
